#include "src\precompiled.h"
#include "src\maths.h"
#include "src\model.h"
#include "src\mesh.h"
#include "src\material.h"
#include "src\texture.h"
#include "src\animation.h"
#include "src\skeleton.h"
#include "src\xray_scene_saver.h"
#include "xr_object.h"
#include "xr_mesh.h"

static inline bool save_points(mesh_ptr m, std::vector<xray_re::fvector3>& points, xray_re::fbox &bbox, const matrix4 &tm)
{
	xray_re::fvector3 v;
	for(size_t j = 0; j < m->get_num_vertices(); j++) {
		vector3 p = tm * m->get_coord(j);
		v.set(p.x, p.z, -p.y);
		bbox.extend(v);
		points.push_back(v);
	}
	return points.size();
}

static inline bool save_faces(mesh_ptr m, xray_re::lw_face_vec &faces)
{
	for(size_t j = 0; j < m->get_num_indices() / 3; j++) {
		size_t v0 = m->get_idx(j * 3);
		size_t v1 = m->get_idx(j * 3 + 1);
		size_t v2 = m->get_idx(j * 3 + 2);
		xray_re::lw_face face(v0, v1, v2);
		faces.push_back(face);
	}
	return faces.size();
}

static inline bool save_uv_map(mesh_ptr m, xray_re::xr_vmap_vec &vmaps,
	xray_re::lw_vmref_vec& vmrefs, xray_re::lw_face_vec &faces)
{
	if(m->get_num_indices() < 3)
		return false;
	xray_re::xr_uv_vmap *uvs = new xray_re::xr_uv_vmap("Texture");
	vmaps.push_back(uvs);
	for(size_t j = 0; j < m->get_num_indices() / 3; j++) {
		xray_re::lw_face &f = faces[j];
		for(int k = 0; k < 3; k++) {
			size_t idx = m->get_idx(j*3+k);
			vector2 uv = m->get_texcoord(idx);
			xray_re::fvector2 xruv;
			xruv.set(-uv.x, uv.y);

			xray_re::lw_vmref vmref;
			vmref.push_back(xray_re::lw_vmref_entry(0, uvs->add_uv(xruv, idx)));
			size_t vmref_idx = vmrefs.size();
			vmrefs.push_back(vmref);

			f.ref[k] = vmref_idx;
		}
	}
	return true;
}

static inline bool save_surface(mesh_ptr m, material_ptr mat,
	xray_re::xr_surfmap_vec &surfmaps, xray_re::xr_surface_vec &surfaces)
{
	if(m->get_num_indices() < 3 || mat.is_null())
		return false;
	texture_ptr tex = mat->get_diffuse();
	if(tex.is_null())
		return false;
	xray_re::xr_surface* surface = new xray_re::xr_surface(false);
	surfaces.push_back(surface);
	surface->name() = m->get_name();
	surface->gamemtl() = "default";
	surface->cshader() = "default";
	surface->eshader() = "default";
	surface->texture() = tex->get_name();
	xray_re::xr_surfmap* smap = new xray_re::xr_surfmap(surface);
	surfmaps.push_back(smap);
	for(size_t j = 0; j < m->get_num_indices() / 3; j++) {
		smap->faces.push_back(j);
	}
	return true;
}

static inline bool save_sgroups(mesh_ptr m, std::vector<uint32_t> &sgroups)
{
	// FIXME: not implemented
	sgroups.assign(m->get_num_indices() / 3, 0x02);
	return true;
}

bool xray_scene_saver::save(const char *path)
{
	// FIXME:	there are two problems:
	//			1 - dynamic meshes not yet supported
	//			2 - groups not supported and child nodes cannot be exported because i don't
	//				understand how i should save both static and nonstatic child objects,
	//				and dynamic objects with different skeleton objects linked to them.

	xray_re::xr_object obj;
	obj.flags() = xray_re::EOF_STATIC;
	for(size_t i = 0; i < get_num_models(); i++) {
		model_ptr m = get_model(i);
		matrix4 tm = m->get_transforms();
		material_ptr mat;
		for(size_t j = 0; j < m->get_num_meshes(); j++) {
			mesh_ptr mesh = m->get_mesh(j);
			if(j < m->get_num_materials())
				mat = m->get_material(j);
			xray_re::xr_mesh *xrm = new xray_re::xr_mesh;
			obj.meshes().push_back(xrm);
			xrm->name() = mesh->get_name();
			if(!save_points(mesh, xrm->points(), xrm->bbox(), tm))
				return false;
			if(!save_faces(mesh, xrm->faces()))
				return false;
			if(!save_uv_map(mesh, xrm->vmaps(), xrm->vmrefs(), xrm->faces()))
				return false;
			if(!save_surface(mesh, mat, xrm->surfmaps(), obj.surfaces()))
				return false;
			if(!save_sgroups(mesh, xrm->sgroups()))
				return false;
		}
	}
	obj.save_object(path);
	return true;
}